// SQLiteMap.cc
// author: dipina@eside.deusto.es
// compile: g++ SQLiteMap.cc -lsqlite3 -o SQLiteMap
#include "SQLiteMap.h"

#include <fstream>
#include <iostream>
#include <string>

extern "C" {
	#include <sqlite3.h>	
	#include <stdio.h>
}
namespace PracticaCaso {
	SQLiteMap::SQLiteMap(string fn): fileName(fn), dbh(0) {
		
		// Process the contents of the mapping file
		cout << "Loading from DB to SqliteMap" << endl;
        this->loadMappings(fn);
	}

	SQLiteMap::SQLiteMap(const SQLiteMap& rhs) {
		//MODI rhs 
		fileName = rhs.fileName;
		dbh = rhs.dbh;
		this->loadMappings(fileName);
	}

	SQLiteMap::~SQLiteMap() {
		cout << "SQLiteMap: destructor called " << endl;
		this->close();
	}

	void SQLiteMap::loadMappings(string mappingsDBFileName) {
		// Loads the mappings stored at SQLite DB into the map loadMappings
		 
                //MODI tomado del ejemplo sqlite3
				char **result;
                int nrow;
                int ncol;
                char *errorMsg;
		
		 cout << "Opening KeyValuePair database ..." << endl;
		 
		 //MODI sqlite3_open(const char *filename,sqlite3 **ppdb)
		 //MODI **ppdb es un puntero a un puntero tipo "sqlite3"
                if (sqlite3_open(mappingsDBFileName.c_str(), &this->dbh) != SQLITE_OK) {
                 cout << "KeyValuePair DB can not be opened ..." << endl;
                 exit(1);
				 }
		// In the case that the DB does not exist, create it, its structure is given by file KeyValueDB.sql
		// If a select * from KeyValuePair executed through a sqlite3_get_table does not return SQLITE_OK, it means that the table does not exist, and needs being created
		 
		 //MODI tomado del ejemplo sqlite3 
		 if (sqlite3_get_table(this->dbh, "select * from KeyValuePair", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) {
                        // table doesn't exist
                        cerr << errorMsg << endl;
                        sqlite3_free(errorMsg);
                        if (sqlite3_get_table(this->dbh, "create table KeyValuePair(key_element BLOB NOT NULL PRIMARY KEY, value_element BLOB)", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) {
                                cerr << errorMsg << endl;
                                sqlite3_free(errorMsg);
                                sqlite3_close(this->dbh);
								
								//PRUEBA textual ejemplo exit(1);
								
                        } else {
                                cout << "Table KayValuePair created" << endl;
                        }
                } else {
                        // load DB into the SQLitemap
                        cout << "inserting values to SQLiteMap from DB..." << endl;
                        cout << "number of rows: " << nrow << endl;
                        for (int i=1; i<=nrow; i++) {
                                cout << "I'm loading into the map: " <<
                                                string(result[i*ncol]) + "--" + string(result[i*ncol+1]) << endl;
                                this->dns2IpPortMap[string(result[i*ncol])] = string(result[i*ncol+1]);
                        }
                        sqlite3_free_table(result);
                } 
		//MODI Todo lo de arriba copiado del ejemplo de sqlite3  ajustando variables y texto
		// If there are unexpected error exit the program with exit(1)
	}

	
	map<string, string> SQLiteMap::getMap() {
		//MODI 2.3.8
		return this->dns2IpPortMap;
	}


	string SQLiteMap::get(string key) {
		 //MODI 2.3.8
		 //buscamos el valor pasado por parametro "key" y devuelve "ERROR" si no encuentra  o "ip:puerto"
		 map<string, string>::iterator i;
                i = this->dns2IpPortMap.find(key);        
                if (i != this->dns2IpPortMap.end())
                        return i->second;
                else
                        return "ERROR";
	}

	void SQLiteMap::set(string mapKey, string mapValue) {
		// Undertake the update of the STL map and the database. Bear in mind that it there is not an entry with a passed key an INSERT will have to be executed, if there was already such an entry an UPDATE will take place
	//**2.3.8
	//MODI 2.3.8
    //GAURDA una nueva entrada en el mapa que actua como cache
	            
				//MODI Ejemplo sqlite3
				char **result;
                int nrow;
                int ncol;
                char *errorMsg;
                
                map<string, string>::iterator i;
                i = this->dns2IpPortMap.find(mapKey);
				//MODI iterator =....find(map...)
				//MODI Searches the container for an element with a key equivalent to k and returns an iterator to it if found, otherwise it returns an iterator to map::end.
                if (i == this->dns2IpPortMap.end()) {
                        // It doesnt exist in the map, we have to insert into DB
                        cout << "\n Inserting in the DB " << mapKey << " and " << mapValue << endl;
						//MODI Ejemplo sqlite3
						//No sabemos porque pero no nos coje la sentencia directamente. la passamos a un String con y a char** con .c_str()
						string sentenciaSQL="insert into KeyValuePair values('" + mapKey + "', '" + mapValue + "')";
					    if (sqlite3_get_table(this->dbh,sentenciaSQL.c_str() , &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
						{cerr << errorMsg << endl;
                        sqlite3_free(errorMsg);
                        } else {
                      
                        cout << "\nTable KayValuePair inserted" << endl;
                        sqlite3_free_table(result);
						}
				
				
				} else {
                        // It already exists in the map, we have to update the DB
                        cout << "\n updating " << mapKey << " and " << mapValue << endl;
                        //MODI Ejemplo sqlite3
						//No sabemos porque pero no nos coje la sentencia directamente. la pasamos a un String con y a char** con .c_str()
						string sentenciaSQL="update KeyValuePair set value_element='" + mapValue + "' where key_element='" + mapKey + "'";
						if (sqlite3_get_table(this->dbh,sentenciaSQL.c_str(), &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
						{cerr << errorMsg << endl;
                        sqlite3_free(errorMsg);
                        } else {
                        cout << "\nTable KeyValuePair updated" << endl;
                        sqlite3_free_table(result);
						}
					}   
sqlite3_close(this->dbh);					
                //MODI Access element mapped_type& operator[] (const key_type& k);
//If k matches the key of an element in the container, the function returns a reference to its mapped value.
//If k does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value.
  this->dns2IpPortMap[mapKey] = mapValue;
}


	void SQLiteMap::close() {
		// Close the database properly
	//MODI 2.3.8
	sqlite3_close(this->dbh);
	cout<<"Database closed"<<endl;
	}

	ostream & operator << (ostream & os, SQLiteMap &t) {
		os << "DB file name: " << t.fileName << endl;
		os << "DNS mappings:" << endl;
		typedef map<string, string>::const_iterator CI;
		for (CI p = t.dns2IpPortMap.begin(); p != t.dns2IpPortMap.end(); ++p) {
			os <<  p->first << " : " << p->second << endl;
		}
		return os;
	}
}


/*
// global variable
PracticaCaso::SQLiteMap * SQLiteMap_pointer;

// function called when CTRL-C is pressed
void ctrl_c(int)
{
    printf("\nCTRL-C was pressed...\n");
	delete SQLiteMap_pointer;
}


void usage() {
	cout << "Usage: SQLiteMap <name-mappings-db-file>" << endl;
	exit(1);
}

int main(int argc, char** argv) {
	signal(SIGINT,ctrl_c);

	if (argc != 2) {
		usage();
	}

	PracticaCaso::SQLiteMap * SQLiteMap = new PracticaCaso::SQLiteMap((string)argv[1]);
	cout << "SQLiteMap instance: " << endl << SQLiteMap << endl;
	//SQLiteMap_pointer = &SQLiteMap;
	SQLiteMap_pointer = SQLiteMap;

	//SQLiteMap.set("saludo", "hola");
	//SQLiteMap.set("despedida", "adios");
	SQLiteMap->set("saludo", "hola");
	SQLiteMap->set("despedida", "adios");

	//cout << "SQLiteMap[\"saludo\"] = " << SQLiteMap.get("saludo") << endl;
	//cout << "SQLiteMap[\"despedida\"] = " << SQLiteMap.get("despedida") << endl;
	cout << "SQLiteMap[\"saludo\"] = " << SQLiteMap->get("saludo") << endl;
	cout << "SQLiteMap[\"despedida\"] = " << SQLiteMap->get("despedida") << endl;

	cout << "SQLiteMap instance after 2 sets: " << endl << *SQLiteMap << endl;

	//map<string, string> savedSQLiteMap = SQLiteMap.getMap();
	map<string, string> savedSQLiteMap = SQLiteMap->getMap();
	cout << "Recuperado el mapa" << endl;
	typedef map<string, string>::const_iterator CI;
	for (CI p = savedSQLiteMap.begin(); p != savedSQLiteMap.end(); ++p) {
		cout << "dns2IpPortMap[" << p->first << "] = " << p->second << endl;
	}
	
	delete SQLiteMap_pointer;
}
*/

